﻿#include "Trainer.h"
#include "../../../Shared/Data/Stat.h"

using namespace Server::MirDatabase;
namespace S = ServerPackets;

namespace Server::MirObjects::Monsters
{

    bool Trainer::getCanAttack() const
    {
        return false;
    }

    bool Trainer::getCanMove() const
    {
        return false;
    }

    bool Trainer::DropItem(UserItem *item)
    {
        throw NotSupportedException();
    }

    bool Trainer::DropGold(unsigned int gold)
    {
        throw NotSupportedException();
    }

    void Trainer::Attack()
    {
    }

    void Trainer::ProcessRoam()
    {
    }

    bool Trainer::getBlocking() const
    {
        return true;
    }

    bool Trainer::IsAttackTarget(PlayerObject *attacker)
    {
        return true;
    }

    bool Trainer::IsAttackTarget(MonsterObject *attacker)
    {
        if (attacker->Master == nullptr)
        {
            return false;
        }

        return true;
    }

    void Trainer::Die()
    {
    }

    void Trainer::ReceiveChat(const std::string &text, ChatType type)
    {
    }

    Trainer::Trainer(MonsterInfo *info) : MonsterObject(info)
    {
    }

    void Trainer::Spawned()
    {
        PoisonStopRegen = false;
        if (Respawn != nullptr && Respawn->Info->Direction < 8)
        {
            setDirection(static_cast<MirDirection>(Respawn->Info->Direction));
        }

        MonsterObject::Spawned();
    }

    void Trainer::Process()
    {
        MonsterObject::Process();

        if (_currentAttacker != nullptr && _lastAttackTime + 5000 < getEnvir()->getTime())
        {
            OutputAverage();
            ResetStats();
        }
    }

    int Trainer::Attacked(PlayerObject *attacker, int damage, DefenceType type, bool damageWeapon)
    {
        if (attacker == nullptr)
        {
            return 0;
        }

        if (_currentAttacker != nullptr && _currentAttacker != attacker)
        {
            OutputAverage();
            ResetStats();
        }

        damage += attacker->Stats[Stat::AttackBonus];

        int armour = 0;
        //deal with trainers defense
        switch (type)
        {
            case DefenceType::AC:
            case DefenceType::ACAgility:
                armour = GetAttackPower(Stats[Stat::MinAC], Stats[Stat::MaxAC]);
                break;
            case DefenceType::MAC:
            case DefenceType::MACAgility:
                armour = GetAttackPower(Stats[Stat::MinMAC], Stats[Stat::MaxMAC]);
                break;
        }
        if (armour >= damage)
        {
            BroadcastDamageIndicator(DamageType::Miss);
            return 0;
        }

        damage -= armour;

        attacker->GatherElement();

        if (_currentAttacker == nullptr)
        {
            _StartTime = getEnvir()->getTime();
        }
        _currentAttacker = attacker;
        _hitCount++;
        _totalDamage += damage;
        _lastAttackTime = getEnvir()->getTime();

        ReportDamage(damage, type, false);
        return 1;
    }

    int Trainer::Attacked(MonsterObject *attacker, int damage, DefenceType type)
    {
        if (attacker == nullptr || attacker->Master == nullptr)
        {
            return 0;
        }

        if (_currentAttacker != nullptr && _currentAttacker != attacker->Master)
        {
            OutputAverage();
            ResetStats();
        }



        int armour = 0;
        //deal with trainers defense
        switch (type)
        {
            case DefenceType::AC:
            case DefenceType::ACAgility:
                armour = GetAttackPower(Stats[Stat::MinAC], Stats[Stat::MaxAC]);
                break;
            case DefenceType::MAC:
            case DefenceType::MACAgility:
                armour = GetAttackPower(Stats[Stat::MinMAC], Stats[Stat::MaxMAC]);
                break;
        }
        if (armour >= damage)
        {
            BroadcastDamageIndicator(DamageType::Miss);
            return 0;
        }
        damage -= armour;

        if (_currentAttacker == nullptr)
        {
            _StartTime = getEnvir()->getTime();
        }


        MapObject *tmpAttacker = attacker->Master;

        while (true)
        {
            if (tmpAttacker->Master != nullptr)
            {
                tmpAttacker = tmpAttacker->Master;
                continue;
            }
            break;
        }

        _currentAttacker = static_cast<PlayerObject*>(tmpAttacker);

        _hitCount++;
        _totalDamage += damage;
        _lastAttackTime = getEnvir()->getTime();

        ReportDamage(damage, type, true);
        return 1;
    }

    int Trainer::Struck(int damage, DefenceType type)
    {
        return 0;
    }

    void Trainer::PoisonDamage(int damage, MapObject *attacker)
    {
        damage = damage * (-1);
        if (attacker == nullptr || (dynamic_cast<MonsterObject*>(attacker) != nullptr && attacker->Master == nullptr))
        {
            return;
        }

        if (_currentAttacker != nullptr && (_currentAttacker != attacker || _currentAttacker != attacker->Master))
        {
            OutputAverage();
        }

        if (_currentAttacker == nullptr)
        {
            _StartTime = getEnvir()->getTime();
        }
        _currentAttacker = dynamic_cast<MonsterObject*>(attacker) != nullptr ? static_cast<PlayerObject*>(attacker->Master) : static_cast<PlayerObject*>(attacker);
        _hitCount++;
        _totalDamage += damage;
        _lastAttackTime = getEnvir()->getTime();

        long long timespend = std::max(1000, (getEnvir()->getTime() - _StartTime)); //avoid division by 0
        if (_StartTime == 0)
        {
            timespend = 1000;
        }
        double Dps = _totalDamage / (timespend * 0.001);
        _currentAttacker->ReceiveChat(std::string::Format("{1} inflicted {0} Damage, Dps: {2:#.00}.", damage, dynamic_cast<MonsterObject*>(attacker) != nullptr ? "Your pets poison" : "Your poison", Dps), ChatType::Trainer);
        Poisoned = true;
    }

    void Trainer::ProcessRegen()
    {
        if (Dead)
        {
            return;
        }

        int healthRegen = 0;

        if (getCanRegen())
        {
            RegenTime = getEnvir()->getTime() + RegenDelay;
            healthRegen += static_cast<int>(Stats[Stat::HP] * 0.022F) + 1;
        }
        if (healthRegen > 0)
        {
            ChangeHP(healthRegen);
        }
    }

    void Trainer::ChangeHP(int amount)
    {
        if (!Poisoned)
        {
            return;
        }
        if (_currentAttacker == nullptr)
        {
            return;
        }
        _totalDamage += amount;
        long long timespend = std::max(1000, (getEnvir()->getTime() - _StartTime)); //avoid division by 0
        if (_StartTime == 0)
        {
            timespend = 1000;
        }
        double Dps = _totalDamage / (timespend * 0.001);
        _currentAttacker->ReceiveChat(std::string::Format("Your poison stopped {0} regen, Dps: {1:#.00}.", amount, Dps), ChatType::Trainer);
    }

    void Trainer::ReportDamage(int damage, DefenceType type, bool Pet)
    {
        std::string output = "";
        switch (type)
        {
            case DefenceType::ACAgility:
                output = "Physical Agility";
                break;
            case DefenceType::AC:
                output = "Physicial";
                break;
            case DefenceType::MACAgility:
                output = "Magical Agility";
                break;
            case DefenceType::MAC:
                output = "Magic";
                break;
            case DefenceType::Agility:
                output = "Agility";
                break;
        }
        long long timespend = std::max(1000,(getEnvir()->getTime() - _StartTime)); //avoid division by 0
        if (_StartTime == 0)
        {
            timespend = 1000;
        }
        double Dps = _totalDamage / (timespend * 0.001);
        _currentAttacker->ReceiveChat(std::string::Format("{3} inflicted {0} {1} Damage, Dps: {2:#.00}.", damage, output, Dps, Pet? "Your pet": "You"), ChatType::Trainer);
    }

    void Trainer::ResetStats()
    {
        _currentAttacker = nullptr;
        _hitCount = 0;
        _totalDamage = 0;
        _lastAttackTime = 0;
        _StartTime = 0;
        Poisoned = false;
        PoisonList.clear();
    }

    void Trainer::OutputAverage()
    {
        if (_currentAttacker == nullptr)
        {
            return;
        }
        long long timespend = std::max(1000, (_lastAttackTime - _StartTime)); //avoid division by 0
        if (_StartTime == 0)
        {
            timespend = 1000;
        }
        double Dps = _totalDamage / (timespend * 0.001);
        _currentAttacker->ReceiveChat(std::string::Format("{0} Average Damage inflicted on the trainer, Dps: {1:#.00}.", static_cast<int>(_totalDamage / _hitCount),Dps), ChatType::Trainer);
    }
}
